#ifndef TRACKER_H
#define TRACKER_H

#include "camshift_wrapper.h"
#include <highgui.h>
#include <utility>
#include "chronometer.h"
#include <queue>

class tracker
{
  size_t period;
  mutable cv::VideoCapture cam;
  camshift_wrapper cs;
  //std::queue<cv::Mat> frames;
  cv::Mat last_frame;
  bool transpose_and_flip;
  cv::RotatedRect track_box;
  cv::Size range;
  mutable CRITICAL_SECTION crit;
  mutable CRITICAL_SECTION crit_frame;
  cv::Point2f bias;
  cv::Point2f full_scale;
  HANDLE thread;
  HANDLE cam_thread;
  HANDLE sem_consumed;
  HANDLE sem_produced;
  unsigned int threadId;
  HANDLE timer;
  //HANDLE timer2;
  chronometer chr;
  chronometer cam_chr;

  friend unsigned int WINAPI tracker_thread(void* arg);
  friend unsigned int WINAPI tracker_cam_thread(void* arg);
  
  void track(const cv::Mat& frame)
  {
    EnterCriticalSection(&crit);
    track_box = cs.track(frame);
    range = frame.size();
    LeaveCriticalSection(&crit);
  }

  void produce_frame();

  cv::Mat consume_frame();

public:
  tracker(size_t period=100, bool transpose_and_flip=true, cv::Point2f bias=cv::Point2f(1,1), cv::Point2f full_scale=cv::Point2f(0,0));
  ~tracker();

  void start_tracking(const cv::Mat& img, const cv::Rect& selection)
  {
    cs.start_tracking(img,selection);
  }

  const camshift_wrapper& get_camshift_wrapper() const
  {
    return cs;
  }
  camshift_wrapper& get_camshift_wrapper() 
  {
    return cs;
  }
  const cv::VideoCapture& get_cam() const
  {
    return cam;
  }

  cv::RotatedRect get_track_box() const
  {
    cv::RotatedRect ret;
    EnterCriticalSection(&crit);
    ret = track_box;
    LeaveCriticalSection(&crit);
    return ret;
  }
  cv::Size get_range() const
  {
    cv::Size ret;
    EnterCriticalSection(&crit);
    ret = range;
    LeaveCriticalSection(&crit);
    return ret;
  }
  std::pair<cv::RotatedRect, cv::Size> get_track_box_and_range() const
  {
    std::pair<cv::RotatedRect, cv::Size> ret;
    EnterCriticalSection(&crit);
    ret = std::make_pair(track_box,range);
    LeaveCriticalSection(&crit);
    return ret;
  }
  
  cv::Mat get_frame()
  {
    cv::Mat ret;
    EnterCriticalSection(&crit_frame);
    last_frame.copyTo(ret);
    //frames.front().copyTo(ret);
    LeaveCriticalSection(&crit_frame);
    return ret;
  }

  void adjust_bias(cv::Point2f bias)
  {
    this->bias=bias;
  }
  void adjust_full_scale(cv::Point2f full_scale)
  {
    this->full_scale=full_scale;
  }

  cv::Point2f get_bias() const
  {
    return bias;
  }
  cv::Point2f get_full_scale() const
  {
    return full_scale;
  }

  // if coordinate is between bias and full_scale, return will be a number between 0 and 1
  std::pair<double,double> pos() const
  {
    std::pair<cv::RotatedRect, cv::Size> tb_r = get_track_box_and_range();
    cv::Point2f abs_pos (tb_r.first.center.x/tb_r.second.width, tb_r.first.center.y/tb_r.second.height);
    double x = (static_cast<double>(abs_pos.x) - bias.x)/(full_scale.x - bias.x);
    double y = (static_cast<double>(abs_pos.y) - bias.y)/(full_scale.y - bias.y);
    return std::make_pair(x,y);
  }

  chronometer get_chronometer() const
  {
    return chr;
  }

  chronometer get_cam_chronometer() const
  {
    return cam_chr;
  }
};

#endif

